package com.ouc.eddyAlgorithm.service.impl;


import com.ouc.common.constant.VariableName;
import com.ouc.common.entity.NcData;
import com.ouc.common.entity.NcRecord;
import com.ouc.common.ncRead.NcDao;
import com.ouc.common.utils.ArrayUtils;
import com.ouc.common.utils.MathUtils;
import com.ouc.eddyAlgorithm.entity.EddyCenterData;
import com.ouc.eddyAlgorithm.service.EddyAlgorithmCenterService;
import com.ouc.visualization.entity.NcTerrain;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import ucar.ma2.InvalidRangeException;
import ucar.nc2.NetcdfFile;

import java.io.IOException;
import java.util.Map;

import static com.ouc.common.constant.NcConstants.*;
import static java.lang.Float.NaN;

@Service
public class EddyAlgorithmCenterServiceImpl implements EddyAlgorithmCenterService {

  @Autowired
  private NcDao ncDao;


  @Override
  public EddyCenterData getEddyCenterData(String filename, String variableName, Float[] lon, Float[] lat) throws IOException, InvalidRangeException {
    return getEddyCenterData(filename, variableName, lon, lat, null);
  }

  @Override
  public EddyCenterData getEddyCenterData(String filename, String variableName, Float[] lon, Float[] lat, Integer[] level) throws IOException, InvalidRangeException {
    NetcdfFile file = NetcdfFile.open(filename);
    Map<String, NcData> map = ncDao.getNcDataWithLimit(file, variableName, lon, lat);
    float[] levelArr = map.get("depth").getData1D();
    float[] lonArr = map.get("lon").getData1D();
    float[] latArr = map.get("lat").getData1D();
    latArr = ArrayUtils.arr1DDilution(latArr, 2);
    float[][][] values = new float[levelArr.length][latArr.length][lonArr.length];
    int startLevel = level == null ? 0 : level[0] == null ? 0 : level[0];
    int endLevel = level == null ? levelArr.length : level[1] == null ? levelArr.length : level[1];
    for (int i = startLevel; i < endLevel; i++) {
      values[i] = ArrayUtils.arr2DDilution(ncDao.getNcDataWithLimit(file, variableName, lon, lat, i).get(variableName).getData2D(), 2, 0);
    }
    return new EddyCenterData(lonArr, latArr, levelArr, values);
  }

  @Override
  public EddyCenterData getEddyCenterDataByLimit(NetcdfFile ncFile, String variableName, Float[] lon, Float[] lat, Integer[] level) throws IOException, InvalidRangeException {
    Map<String, NcData> ncDataMap = ncDao.getNcDataOfLimitFromVariable(ncFile, variableName, lon, lat, 0);
    float[] lonArr = ncDataMap.get(ncDao.getVariable(ncFile, VariableName.LON).getShortName()).getData1D();
    float[] latArr = ncDataMap.get(ncDao.getVariable(ncFile, VariableName.LAT).getShortName()).getData1D();
    float[] levelArr = new float[0];
    if (ncDataMap.get(ncDao.getVariable(ncFile, VariableName.LEVEL).getShortName()).getData1D() != null) {
      levelArr = ncDataMap.get(ncDao.getVariable(ncFile, VariableName.LEVEL).getShortName()).getData1D();
    }
    float[][][] values = new float[levelArr.length][latArr.length][lonArr.length];
    int startLevel = level == null ? 0 : level[0] == null ? 0 : level[0];
    int endLevel = level == null ? levelArr.length : level[1] == null ? levelArr.length : level[1];
    for (int i = startLevel; i < endLevel; i++) {
      values[i] = ncDao.getNcDataOfLimitFromVariable(ncFile, variableName, lon, lat, i)
          .get(ncDao.getVariable(ncFile, variableName).getShortName()).getData2D();
    }
    return new EddyCenterData(lonArr, latArr, levelArr, values);
  }

  @Override
  public NcTerrain getNcIsosurfaces(NetcdfFile ncFile, String variableName, Float[] lon, Float[] lat, float standardValue, float interval) throws IOException, InvalidRangeException {
    float[] depth = ncDao.getLevel(ncFile);
    float start = standardValue - interval;
    float end = standardValue + interval;
    if (!variableName.equals(CURRENTUV)) {
      NcRecord ncRecord = ncDao.getData3DWithLimit(ncFile, variableName, lon, lat);
      float[][][] data3D = ncRecord.getData3D();
      float[] lonLimit = ncDao.getNcGridData(ncFile, variableName, lon, lat).getListLon();
      float[] latLimit = ncDao.getNcGridData(ncFile, variableName, lon, lat).getListLat();
      float[][][] data3DSuction = ArrayUtils.data3DSuction(data3D);
      float[][] res = new float[data3DSuction.length][data3DSuction[0].length];
      for (int i = 0; i < data3DSuction.length; i++) {
        for (int j = 0; j < data3DSuction[i].length; j++) {
          int firstNaN = getFirstNaN(data3DSuction[i][j], start, end);
          if (firstNaN < 0) {
            res[i][j] = NaN;
          } else {
            res[i][j] = depth[firstNaN];
          }

        }
      }
      return new NcTerrain(res, lonLimit, latLimit, depth);
    } else {
      float[][][] data3DU = ncDao.getData3DWithLimit(ncFile, UU, lon, lat).getData3D();
      float[][][] data3DV = ncDao.getData3DWithLimit(ncFile, VV, lon, lat).getData3D();
      float[][][] data3D = new float[data3DU.length][][];
      for (int i = 0; i < data3DU.length; i++) {
        data3D[i] = ArrayUtils.getDataFromUV(data3DU[i], data3DV[i]);
      }
      float[] lonLimit = ncDao.getNcGridData(ncFile, variableName, lon, lat).getListLon();
      float[] latLimit = ncDao.getNcGridData(ncFile, variableName, lon, lat).getListLat();
      float[][][] data3DSuction = ArrayUtils.data3DSuction(data3D);
      float[][] res = new float[data3DSuction.length][data3DSuction[0].length];
      for (int i = 0; i < data3DSuction.length; i++) {
        for (int j = 0; j < data3DSuction[i].length; j++) {
          int firstNaN = getFirstNaN(data3DSuction[i][j], start, end);
          if (firstNaN < 0) {
            res[i][j] = NaN;
          } else {
            res[i][j] = depth[firstNaN];
          }

        }
      }
      return new NcTerrain(res, lonLimit, latLimit, depth);
    }

  }

  /**
   * 获取第一个在数据范围内的的索引
   *
   * @param arr 目标数组
   * @return
   */
  private int getFirstNaN(float[] arr, float min, float max) {
    for (int i = 0; i < arr.length; i++) {
      if (MathUtils.between(arr[i], min, max)) {
        return i;
      }
    }
    return -1;
  }
}
